<template>
  <div id="global-webrtc">
    <div class="webrtc-box" :class="aligenLeft ? 'aligenLeft' : ''">
      <div
        v-for="(stream, index) in streams"
        :key="index"
        class="stream-box"
        :class="stream.local ? 'local' : 'remote'"
      >
        <div class="empty-msg">
          <!-- 本机用户 -->
          <span v-if="stream.local" class="text">
            <span v-if="localStream.ok">Long time no see.</span>
            <span v-else>Ice 失败</span>
          </span>
          <!-- 远程用户 -->
          <span v-else class="text">{{ stream.name }}</span>
        </div>
        <div class="content-box" :class="`filter-${filters[stream.filter]}`">
          <video
            :id="stream.id"
            :ref="stream.id"
            autoplay
            playsinline
            preload="auto"
            :src="stream.src"
            :width="getVideoElementSize(index).width"
            :height="getVideoElementSize(index).height"
          />
          <!-- 当前视频的美颜功能是否开启 -->
          <face-ctracker
            v-if="!localStream.disabledCarema && !!stream.beauty"
            class="face-mask"
            :ref-id="stream.id"
            :width="getVideoElementSize(index).width"
            :height="getVideoElementSize(index).height"
          />
        </div>
        <div v-if="!stream.local" class="name">
          <span>{{ stream.name }}</span>
          <span class="state">
            <span v-if="!stream.state">正在赶来的路上</span>
            <span v-else-if="stream.state === stream.checking">连接中</span>
            <span v-else-if="stream.state === stream.connected">连接成功</span>
            <span v-else-if="stream.state === stream.disconnected">失败</span>
            <span v-else-if="stream.state === stream.closed">已离开</span>
            <span v-else-if="stream.state === stream.failed">各种失败，来不了了</span>
          </span>
        </div>
        <meter
          class="volume"
          min="-45"
          max="-20"
          high="-25"
          low="-40"
          :value="stream.local ? localStream.volume : stream.volume"
        ></meter>
        <!-- 仅本机用户 -->
        <div v-if="stream.local" class="tools">
          <button
            class="video"
            :class="{ active: !localStream.disabledCarema }"
            @click="toggleCarema(!localStream.disabledCarema)"
          >
            <i class="iconfont" :class="localStream.disabledCarema ? 'icon-carema-disabled' : 'icon-carema'"></i>
          </button>
          <button
            class="audio"
            :class="{ active: !localStream.disabledMic }"
            @click="toggleMute(!localStream.disabledMic)"
          >
            <i class="iconfont" :class="localStream.disabledMic ? 'icon-mic-disabled' : 'icon-mic'"></i>
          </button>
          <button
            class="beauty"
            :class="{ active: !localStream.disabledBeauty }"
            @click="toggleBeauty()"
          >
            <i class="iconfont icon-face-ai"></i>
            <span>美颜</span>
          </button>
          <div class="filter" :class="{ active: stream.filter !== 0 }">
            <span class="current">
              <i class="iconfont icon-filter"></i>
              <span class="name">{{ filters[stream.filter] }}</span>
            </span>
            <ul class="filter-list">
              <li v-for="(filter, _index) in filters" :key="_index" class="item" @click="sendFilterToAll(_index)">
                <span>{{ filter }}</span>
              </li>
            </ul>
          </div>
        </div>
        <!-- 仅远程媒体 -->
        <div v-else class="tools">
          <button class="video" disabled :class="{ active: !stream.disabledCarema }">
            <i class="iconfont" :class="stream.disabledCarema ? 'icon-carema-disabled' : 'icon-carema'"></i>
          </button>
          <button class="audio" disabled :class="{ active: !stream.disabledMic }">
            <i class="iconfont" :class="stream.disabledMic ? 'icon-mic-disabled' : 'icon-mic'"></i>
          </button>
          <button class="beauty" disabled :class="{ active: stream.beauty }">
            <i class="iconfont icon-meiyan"></i>
          </button>
          <span class="filter">
            <span class="current">
              <i class="iconfont icon-filter"></i>
              <span class="name">{{ filters[stream.filter] }}</span>
            </span>
          </span>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
  import { states, config } from './util'
  import { isDevMode, isBrowser } from '~/environment'
  import socketio from '~/plugins/socket.io'
  // https://github.com/andyet/SimpleWebRTC/blob/master/src/simplewebrtc.js#L327
  import SimpleWebRTC from 'simplewebrtc'
  import { getFileCDNUrl } from '~/transformers/url'
  import faceCtracker from './face-ctracker'

  export default {
    name: 'Webrtc',
    components: {
      faceCtracker
    },
    data() {
      return {
        clmtrackrLibLoading: false,
        clmtrackrLoaded: false,
        localStream: {
          ok: true,
          volume: -45,
          peerId: null,
          disabledMic: false,
          disabledCarema: false,
          disabledBeauty: true
        },
        remoteFilters: [],
        remoteBeautys: [],
        streams: [],
        filters: ['normal', 'grayscale', 'sepia', 'hue-rotate', 'invert', 'blur'],
        names: ['吴彦祖', '王祖贤', '刘恺威', '奥黛丽 赫本', '任达华', '陈冠希', '张曼玉', '刘青云', '甄子丹', '刘德华', '张学友', '黎明', '周润发', '王杰', '黄家驹', '吴孟达', '周星驰', '鹿晗', '黄子韬', '李易峰', '薛之谦', '韩红', '张柏芝', '谢霆锋', '成龙', '梁朝伟', '刘嘉玲', '张家辉', '梁家辉', '吴镇宇', '黄秋生', '古天乐', '余文乐']
      }
    },
    methods: {
      // 计算 video 元素的尺寸
      getVideoElementSize(index) {
        if (index === 0) {
          return { width: 540, height: 345 }
        }
        if (index === 1 || index === 2) {
          return { width: 400, height: 345 }
        }
        return { width: 270, height: 200 }
      },
      toggleMute(disable) {
        disable ? this.webrtc.mute() : this.webrtc.unmute()
        this.localStream.disabledMic = disable
      },
      toggleCarema(disable) {
        disable ? this.webrtc.pauseVideo() : this.webrtc.resumeVideo()
        this.localStream.disabledCarema = disable
      },
      toggleBeauty() {
        if (window.clm && window.clm.tracker) {
          this.localStream.disabledBeauty = !this.localStream.disabledBeauty
          if (this.streams.length && this.streams[0] && this.streams[0].local) {
            this.streams[0].beauty = !this.localStream.disabledBeauty
          }
          if (this.localStream.peerId) {
            this.webrtc.connection.connection.emit('webrtc-set-beauty', {
              beauty: this.streams[0].beauty,
              peerId: this.localStream.peerId,
            })
          }
        } else if (!this.clmtrackrLoaded && !this.clmtrackrLibLoading) {
          const self = this
          window.addLoadedTask(() => {
            const clmtrackrScript = document.createElement('script')
            clmtrackrScript.onload = () => {
              self.clmtrackrLibLoading = false
              self.clmtrackrLoaded = true
              alert('人脸识别库加载成功！期待吴彦祖王祖贤的现身~')
              self.toggleBeauty()
            }
            clmtrackrScript.defer = 'defer'
            clmtrackrScript.src = getFileCDNUrl('/scripts/clmtrackr.js')
            document.body.appendChild(clmtrackrScript)
          })
          this.clmtrackrLibLoading = true
          alert('人脸识别库加载中...大概半分钟，稍后再打开美颜~')
        } else {
          alert('不是说过了！人脸识别库加载中...大概半分钟，半分钟很久吗！！!')
        }
      },
      getStreamByPeerId(id) {
        return this.streams.find(stream => stream.id.includes(id))
      },
      // 向所有媒体广播滤镜
      sendFilterToAll(type) {
        if (this.streams.length && this.streams[0] && this.streams[0].local) {
          this.streams[0].filter = type
        }
        // console.log('切换滤镜', this.localStream.peerId, this.localStream);
        if (this.localStream.peerId) {
          this.webrtc.connection.connection.emit('webrtc-set-filter', {
            peerId: this.localStream.peerId,
            filter: type
          })
        }
      }
    },
    computed: {
      states() {
        return states
      },
      aligenLeft() {
        const count = this.streams.length
        return !!(count > 3 && (count - 3) % 5 > 0)
      }
    },
    beforeDestroy() {
      if (this.webrtc) {
        this.webrtc.stopLocalVideo()
        this.webrtc.leaveRoom()
        // this.webrtc.disconnect()
        this.webrtc = null
      }
      this.streams = []
    },
    mounted() {
      let getUserMedia =
        navigator.getUserMedia ||
        navigator.webkitGetUserMedia ||
        navigator.mozGetUserMedia ||
        navigator.msGetUserMedia

      if (!getUserMedia && (navigator.mediaDevices && navigator.mediaDevices.getUserMedia)) {
        getUserMedia = navigator.mediaDevices.getUserMedia
      }

      if (!getUserMedia) {
        window.alert('不支持我有什么办法')
        this.$store.commit('global/toggleUpdateWebcamOnState', false)
        return false
      } else {
        navigator.getUserMedia = getUserMedia
      }

      const self = this
      const room = config.room

      // 用于唯一标识符
      const buildStreamId = peer => `remote-video-${webrtc.getDomId(peer)}`

      // 解析音量变化
      const parseVol = volume => {
        if (volume < -45) volume = -45
        if (volume > -20) volume = -20
        return volume
      }

      // 创建 webrtc 实例连接
      const webrtc = this.webrtc = new SimpleWebRTC({
        socketio: {},
        connection: {
          connection: socketio,
          on: socketio.on,
          disconnect: socketio.disconnect,
          getSessionid: () => socketio.id,
          emit() {
            // TODO: 测试
            Reflect.apply(socketio.emit, socketio, arguments)
            // socketio.emit.apply(socketio, arguments)
          }
        },
        localVideoEl: '',
        remoteVideosEl: '',
        debug: false,
        // debug: isDevMode,
        autoAdjustMic: true,
        autoRequestMedia: true,
        detectSpeakingEvents: true,
        url: self.$API.SOCKET,
        // 自动选择流模式
        // iceTransportPolicy: 'relay',
        peerConnectionConfig: {
          // 自动选择流模式
          // iceTransports: 'relay',
          iceServers: [
            {
              urls: [
                'stun:47.96.19.254:3478',
                'turn:47.96.19.254:3478'
              ],
              username: config.iceServerUsername,
              credential: config.iceServerCredential,
            }
          ]
        }
      })
      // console.log('socketio', socketio, 'connection', webrtc.connection)

      // 远程滤镜改变
      webrtc.connection.connection.on('webrtc-set-filter', filterDetail => {
        const peerId = filterDetail.peerId
        const filter = filterDetail.filter
        const targetStream = self.getStreamByPeerId(peerId)
        if (targetStream) {
          targetStream.filter = filter
        }
      })

      // 远程美颜改变
      webrtc.connection.connection.on('webrtc-set-beauty', beautyDetail => {
        const peerId = beautyDetail.peerId
        const beauty = beautyDetail.beauty
        const targetStream = self.getStreamByPeerId(peerId)
        if (targetStream) {
          targetStream.beauty = beauty
        }
      })

      // 远程滤镜们
      webrtc.connection.connection.emit('webrtc-filters', filters => {
        // console.log('收到 webrtc-filters', filters)
        this.remoteFilters = filters
      })

      // 远程美颜们
      webrtc.connection.connection.emit('webrtc-beautys', beautys => {
        // console.log('收到 webrtc-beautys', beautys)
        this.remoteBeautys = beautys
      })

      // 存储本地流 id
      webrtc.on('connectionReady', sessionId => {
        // console.log('本机 connectionReady')
        self.localStream.peerId = sessionId
      })

      // 本地的一切准备好时
      webrtc.on('readyToCall', () => {
        // console.log('本机一切就绪')
        webrtc.joinRoom(room)
      })

      // 当媒体请求被允许可用时
      webrtc.on('localStream', stream => {
        self.localStream.ok = true
        self.streams.unshift({
          local: true,
          filter: 0,
          beauty: false,
          id: 'localVideo',
          ref: 'localVideo',
          src: window.URL.createObjectURL(stream)
        })

        // 因为组件被激活时，socket 极有可能链接，所以库内部的逻辑转移到此处判断和处理
        // https://github.com/andyet/SimpleWebRTC/blob/master/src/simplewebrtc.js#L441
        if (webrtc.connection.connection.connected) {
          if (webrtc.connection.connection.id) {
            self.localStream.peerId = webrtc.connection.connection.id
            webrtc.sessionReady = true
            webrtc.testReadiness()
            // console.log('搞事情了', webrtc)
          }
        }

        // 模拟远程用户
        /*
        const names = self.names
        const mockStream = {
          src: URL.createObjectURL(stream),
          name: names[Math.floor(Math.random() * names.length)],
          filter: 0,
          volume: 0,
          beauty: true,
          id: 'remoteVideo',
          ref: 'remoteVideo'
        }
        self.streams.push(...[mockStream, mockStream, mockStream, mockStream, mockStream, mockStream, mockStream, mockStream, mockStream, mockStream])
        */
      })

      // 媒体请求被拒绝
      webrtc.on('localMediaError', error => {
        window.alert('切')
        self.$store.commit('global/toggleUpdateWebcamOnState', false)
      })

      // 接收到一个信号源
      webrtc.on('videoAdded', (video, peer) => {
        // console.log('接收新的信号源', peer, self.remoteFilters)
        const filter = self.remoteFilters[peer.id]
        const beauty = self.remoteBeautys[peer.id]
        const id = buildStreamId(peer)
        const names = self.names
        const remoteStream = {
          id: id, 
          ref: id,
          state: states.checking,
          volume: 0,
          filter: filter || 0,
          beauty: beauty || false,
          disabledMic: false,
          disabledCarema: false,
          src: URL.createObjectURL(peer.stream),
          name: names[Math.floor(Math.random() * names.length)]
        }

        // show the ice connection state
        if (peer && peer.pc) {
          peer.pc.on('iceConnectionStateChange', event => {
            const stream = self.getStreamByPeerId(peer.id)
            if (stream) {
              const state = peer.pc.iceConnectionState
              const stateCode = states[state]
              if (stateCode !== undefined) {
                stream.state = stateCode
              }
            }
          })
        }
        self.streams.push(remoteStream)
      })

      // 信号源移除
      webrtc.on('videoRemoved', (video, peer) => {
        const id = buildStreamId(peer)
        const index = self.streams.findIndex(s => s.id === id)
        if (index > -1) {
          self.streams.splice(index, 1)
        }
      })

      // local p2p/ice failure
      webrtc.on('iceFailed', peer => {
        self.localStream.ok = false
      })

      // remote p2p/ice failure
      webrtc.on('connectivityError', peer => {
        // console.log('远程媒体链接失败')
        const targetStream = self.getStreamByPeerId(peer.id)
        if (targetStream) {
          targetStream.state = states.failed
        }
      })

      // 本地信号源音量改变
      webrtc.on('volumeChange', (volume, treshold) => {
        if (String(volume) !== '-Infinity') {
          self.localStream.volume = parseVol(volume)
        }
      })

      // 远程信号源音量改变
      webrtc.on('remoteVolumeChange', (peer, volume) => {
        if (String(volume) !== '-Infinity') {
          const stream = self.getStreamByPeerId(peer.id)
          if (stream) {
            stream.volume = parseVol(volume)
          }
        }
      })

      // 远程的信号源静音
      webrtc.on('mute', data => {
        webrtc.getPeers(data.id).forEach(peer =>  {
          const stream = self.getStreamByPeerId(peer.id)
          if (stream) {
            if (data.name === 'audio') {
              stream.disabledMic = true
            } else if (data.name === 'video') {
              stream.disabledCarema = true
            }
          }
        })
      })

      // 远程的信号源取消静音或视频
      webrtc.on('unmute', data => {
        webrtc.getPeers(data.id).forEach(peer =>  {
          const stream = self.getStreamByPeerId(peer.id)
          if (stream) {
            if (data.name === 'audio') {
              stream.disabledMic = false
            } else if (data.name === 'video') {
              stream.disabledCarema = false
            }
          }
        })
      })

      /*
      webrtc.on('stunservers', stunservers => {
        console.log('client stunservers', stunservers)
      })
      webrtc.on('turnservers', turnservers => {
        console.log('client turnservers', turnservers)
      })
      */
    }
  }
</script>

<style lang="scss" scoped>
  #global-webrtc {
    position: fixed;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    z-index: $z-index-toolbox - 1;
    padding-top: $header-height * 2;
    background-color: $module-hover-bg-darken-20;
    user-select: none;
    @include backdrop-blur();

    > .webrtc-box {
      position: relative;
      margin: 0 auto;
      overflow: auto;
      display: flex;
      flex-wrap: wrap;
      justify-content: center;
      align-items: center;
      max-width: 88%;
      max-height: 88%;
      &.aligenLeft {
        justify-content: start;
      }

      > .stream-box {
        height: auto;
        margin-right: $gap;
        margin-bottom: $gap;
        display: flex;
        justify-content: center;
        position: relative;
        overflow-y: hidden;
        border: $sm-gap solid $module-bg-opacity-5;
        background-color: $module-bg;

        &.local {
          width: 40.5%;
          height: 30rem;
        }

        &.remote {
          width: 19%;
          height: 16rem;

          > .volume,
          > .tools {
            opacity: 0.3;
            @include visibility-transition();
          }

          &:hover {
            > .volume,
            > .tools {
              opacity: 1;
            }
          }

          > .volume {
            bottom: 9rem;
            left: -2rem;
            transform: rotate(270deg);
          }

          > .tools {
            > button {
              width: 3rem;
              border-radius: 100%;
            }

            > .filter {
              border-radius: 3rem;
            }
          }
        }

        &:nth-child(2),
        &:nth-child(3) {
          width: 28%;
          height: 30rem;

          > .volume {
            bottom: 2rem;
            left: 18rem;
            transform: none;
            width: calc(100% - 20rem);
          }
        }

        > .content-box {
          display: block;
          width: 100%;
          height: 100%;
          position: relative;

          &.filter-grayscale {
            filter: grayscale(1);
          }
          &.filter-sepia {
            filter: sepia(1);
          }
          &.filter-hue-rotate {
            filter: hue-rotate(180deg);
          }
          &.filter-invert {
            filter: invert(1);
          }
          &.filter-blur {
            filter: blur(1rem);
          }

          > video {
            width: 100%;
            // height: auto;
            object-fit: cover;
          }

          > .face-mask {
            position: absolute;
            width: 100%;
            height: 100%;
            left: 0;
            top: 0;
          }
        }

        > .empty-msg,
        > .tools {
          position: absolute;
        }

        > .empty-msg {
          width: 100%;
          height: 100%;
          display: flex;
          justify-content: center;
          align-items: center;
          font-size: $font-size-h1;
          position: absolute;
          top: 0;
          left: 0;
          z-index: $z-index-underground;

          > .text {
            color: $text-secondary;
          }
        }

        > .name {
          position: absolute;
          top: 0;
          right: 0;
          background-color: $module-bg;
          height: 2.2rem;
          line-height: 2.1rem;
          padding: 0 $gap;
          font-size: $font-size-h4;
          opacity: .6;
        }

        > .volume {
          position: absolute;
          left: $gap;
          bottom: 6rem;
          width: 9rem;
          height: 1rem;
          opacity: .7;
        }

        > .tools {
          display: flex;
          left: $gap;
          bottom: $lg-gap;

          > button,
          > .beauty,
          > .filter {
            width: 4rem;
            margin-right: $gap;
            line-height: 3rem;
            text-align: center;
            background-color: $module-bg;
            text-transform: uppercase;

            &:hover {
              background-color: $module-bg-opacity-8;
            }

            &.active {
              background-color: $module-bg-opacity-9;
            }

            .iconfont {
              font-size: $font-size-h4;
            }
          }

          > .beauty {
            width: auto;
            padding: 0 $gap;

            > .iconfont {
              margin-right: $xs-gap;
            }
          }

          > .filter {
            width: auto;
            padding: 0 $gap;
            margin: 0;
            float: right;
            display: block;
            position: relative;

            &:hover {
              > .filter-list {
                @include visible();
              }
            }

            > .current {
              > .name {
                margin-left: $xs-gap;
              }
            }

            > .filter-list {
              position: absolute;
              bottom: 3rem;
              left: 0;
              width: 100%;
              margin: 0;
              padding: 0;
              list-style: none;
              background-color: $module-bg;
              @include hidden();
              @include visibility-transition();

              > li {
                cursor: pointer;
                @include background-transition();

                &:hover {
                  background-color: $module-bg-opacity-8;
                }
              }
            }
          }
        }
      }
    }
  }
</style>
